#include <stdlib.h>
#include <stdio.h>
#include "../common.h"
#include "bitboard.h"
#include "bitboard_init.h"
#include "bitboard_movelist.h"

// color is color of attacking pieces,
// not of square being attacked
bitboard get_attacks_to(board_t *b, int sq, int color)
{
	bitboard attacks, all_attacks;
	uint8_t contents;
	
	// find attacks on sq from diagonal sliders
	contents = get_diag_contents(b->all_pieces_rl45, sq, coord_map_rl45);
	attacks = diaga8h1_attacks[sq][(contents & 0x7F) >> 1];
	contents = get_diag_contents(b->all_pieces_rr45, sq, coord_map_rr45);
	attacks |= diaga1h8_attacks[sq][(contents & 0x7F) >> 1];
	attacks &= (b->color[color].bishops | b->color[color].queens);
	all_attacks = attacks;


	// find attacks on sq from rect sliders
	contents = get_rank_contents(b->all_pieces, sq);
	attacks = rank_attacks[sq][(contents & 0x7F) >> 1];
	contents = get_file_contents(b->all_pieces_rl90, sq);
	attacks |= file_attacks[sq][(contents & 0x7F) >> 1];
	attacks &= (b->color[color].rooks | b->color[color].queens);
	all_attacks |= attacks;

	all_attacks |= (knight_attacks[sq] & b->color[color].knights);
	all_attacks |= (king_attacks[sq] & b->color[color].king);
	all_attacks |= (pawn_attacks[!color][sq] & b->color[color].pawns);

	return all_attacks;
}

bitboard get_attacks_from(board_t *b, int sq, int piece, int color)
{
	bitboard attacks = 0;
	uint8_t contents;

	switch (piece) {
	case BISHOP:
	case ROOK:
	case QUEEN:
		if (BISHOP_MOVER(piece)) {
			contents = get_diag_contents(b->all_pieces_rl45, sq, coord_map_rl45);
			attacks |= diaga8h1_attacks[sq][(contents & 0x7F) >> 1];
			contents = get_diag_contents(b->all_pieces_rr45, sq, coord_map_rr45);
			attacks |= diaga1h8_attacks[sq][(contents & 0x7F) >> 1];
		}
		if (ROOK_MOVER(piece)) {
			contents = get_rank_contents(b->all_pieces, sq);
			attacks |= rank_attacks[sq][(contents & 0x7F) >> 1];
			contents = get_file_contents(b->all_pieces_rl90, sq);
			attacks |= file_attacks[sq][(contents & 0x7F) >> 1];
		}
		return attacks;
	case KNIGHT:
		attacks = knight_attacks[sq];
		return attacks;
	case KING:
		attacks = king_attacks[sq];
		return attacks;
	case PAWN:
		attacks = pawn_attacks[color][sq];

	default:
		// let PAWN just fall through to here
		return attacks;
	}
}

int find_castles(board_t *b, move_t *move_list, int color)
{
	int count = 0;
	if (b->kscastle[color]) {
		if (color == WHITE &&
		    (b->all_pieces & (piece_setmask[5] | piece_setmask[6])) == 0 &&
		    !get_attacks_to(b, 4, BLACK) && !get_attacks_to(b, 5, BLACK) && !get_attacks_to(b, 6, BLACK)) {
			count++;
			move_list->moving_piece = KING;
			move_list->from = 4;
			move_list->to = 6;
			move_list->captured_piece = 0;
			move_list->new_piece = 0;
			move_list++;
		} else if (color == BLACK &&
		    (b->all_pieces & (piece_setmask[5+56] | piece_setmask[6+56])) == 0 &&
		    !get_attacks_to(b, 4+56, WHITE) && !get_attacks_to(b, 5+56, WHITE) && !get_attacks_to(b, 6+56, WHITE)) {
			count++;
			move_list->moving_piece = KING;
			move_list->from = 4+56;
			move_list->to = 6+56;
			move_list->captured_piece = 0;
			move_list->new_piece = 0;
			move_list++;
		}
	}

	if (b->qscastle[color]) {
		if (color == WHITE &&
		    (b->all_pieces & (piece_setmask[1] | piece_setmask[2] | piece_setmask[3])) == 0 &&
		    !get_attacks_to(b, 2, BLACK) && !get_attacks_to(b, 3, BLACK) && !get_attacks_to(b, 4, BLACK)) {
			count++;
			move_list->moving_piece = KING;
			move_list->from = 4;
			move_list->to = 2;
			move_list->captured_piece = 0;
			move_list->new_piece = 0;
			move_list++;
		} else if (color == BLACK &&
		    (b->all_pieces & (piece_setmask[1+56] | piece_setmask[2+56] | piece_setmask[3+56])) == 0 &&
		    !get_attacks_to(b, 2+56, WHITE) && !get_attacks_to(b, 3+56, WHITE) && !get_attacks_to(b, 4+56, WHITE)) {
			count++;
			move_list->moving_piece = KING;
			move_list->from = 4+56;
			move_list->to = 2+56;
			move_list->captured_piece = 0;
			move_list->new_piece = 0;
			move_list++;
		}
	}

	return count;
}

int find_all_attacks(board_t *b, move_t *move_list, int pawn_move, bitboard target, int color)
{
	int i, move_count = 0;
	int from, to, piece;
	bitboard pb, attacks;


	piece = PAWN;
	for (i=0; i<6; i++) {
		pb = get_piece_board(b, color, piece);

		if (piece == KING) {
			int c;
			
			c = find_castles(b, move_list, color);
			move_count += c;
			move_list += c;
		}

		while (pb) {
			from = last_one_64(pb);

			if (piece == PAWN && pawn_move) {
				attacks = get_attacks_from(b, from, piece, color);
				// for a pawn, an attack is only a move if it's a capture
				attacks &= (b->color[!color].all | b->color[!color].pawn_shadow);
				attacks |= (pawn_moves[color][from] & ~(b->all_pieces));
				if (color == WHITE && (from & 0xF8) == 8 && (piece_setmask[from+8] & b->all_pieces)) {
					attacks &= ~piece_setmask[from+16];
				} else if (color == BLACK && (from & 0xF8) == 48 && (piece_setmask[from-8] & b->all_pieces)) {
					attacks &= ~piece_setmask[from-16];
				}
			} else {
				attacks = get_attacks_from(b, from, piece, color);
			}

			attacks &= target;

			while (attacks) {
				to = last_one_64(attacks);
				move_list->from = from;
				move_list->to = to;
				move_list->moving_piece = piece;
				if (piece == PAWN && (piece_setmask[to] & b->color[!color].pawn_shadow)) {
					move_list->captured_piece = PAWN;
				} else {
					move_list->captured_piece = get_piece_type(b, to, !color);
				}
				if (piece == PAWN && ((to>>3) == 7 || (to>>3) == 0)) {
					move_list->new_piece = QUEEN;
					
					move_list++;
					move_count++;
		
					// also consider underpromotion to knight
					move_list->from = from;
					move_list->to = to;
					move_list->moving_piece = piece;
					move_list->captured_piece = (move_list-1)->captured_piece;
					move_list->new_piece = KNIGHT;

				} else {
					move_list->new_piece = 0;
				}
				move_list++;
				move_count++;
				attacks = BITCLEAR(attacks, to);
			}
			pb = BITCLEAR(pb, from);
		}

		switch (i) {
		case 0:
			piece = ROOK;
			break;
		case 1:
			piece = KNIGHT;
			break;
		case 2:
			piece = BISHOP;
			break;
		case 3:
			piece = QUEEN;
			break;
		case 4:
			piece = KING;
			break;
		default:
			pb = 0;
			break;
		}
	}

	return move_count;
}

int get_capture_list(board_t *b, move_t *move_list, int color)
{
	int move_count;

	move_count = find_all_attacks(b, move_list, 0, b->color[!color].all | b->color[!color].pawn_shadow, color);

	return move_count;
}

int get_move_list(board_t *b, move_t *move_list, int color)
{
	int move_count, total_move_count = 0;
	
	// find captures FIRST
	move_count = find_all_attacks(b, move_list, 0, b->color[!color].all | b->color[!color].pawn_shadow, color);
	move_list += move_count;
	total_move_count += move_count;

	// find the rest
	move_count = find_all_attacks(b, move_list, 1, ~(b->all_pieces), color);
	total_move_count += move_count;

	return total_move_count;
}

int move_piece(board_t *b, move_t m, int color)
{
	board_t old_b;
	bitboard *mover, *opponent;
	int from, to;

	from = m.from;
	to = m.to;

	old_b = *b;

	// no more castles allowed if king is moved
	if (m.moving_piece == KING)
		b->qscastle[color] = b->kscastle[color] = 0;

	// no more castle allowed if rook is moved
	if (m.moving_piece == ROOK && (from == 0 || from == 56)) {
		b->qscastle[color] = 0;
	} else if (m.moving_piece == ROOK && (from == 7 || from == 63)) {
		b->kscastle[color] = 0;
	}

	// no more castle allowed if rook is captured
	if (m.captured_piece == ROOK && (to == 0 || to == 56)) {
		b->qscastle[!color] = 0;
	} else if (m.captured_piece == ROOK && (to == 7 || to == 63)) {
		b->kscastle[!color] = 0;
	}
	
	mover = get_piece_board_p(b, color, m.moving_piece);
	
	if (!m.new_piece) {
		*mover ^= piece_setmask[from] | piece_setmask[to];
	} else {
		// pawn promotion
		*mover ^= piece_setmask[from];
		mover = get_piece_board_p(b, color, m.new_piece);
		*mover |= piece_setmask[to];
	}

	b->color[color].all ^= piece_setmask[from] | piece_setmask[to];

	if (!m.captured_piece) {
		b->all_pieces ^= piece_setmask[from] | piece_setmask[to];
		b->all_pieces_rl90 ^= piece_setmask_rl90[from] | piece_setmask_rl90[to];
		b->all_pieces_rl45 ^= piece_setmask_rl45[from] | piece_setmask_rl45[to];
		b->all_pieces_rr45 ^= piece_setmask_rr45[from] | piece_setmask_rr45[to];
	} else {
		opponent = get_piece_board_p(b, !color, m.captured_piece);

		if (piece_setmask[to] & b->color[!color].pawn_shadow) {
			// en passant capture is handled a bit differently
			int passant;

			if (color == WHITE)
				passant = to - 8;
			else
				passant = to + 8;

			*opponent ^= piece_setmask[passant];
			b->color[!color].all ^= piece_setmask[passant];

			b->all_pieces ^= piece_setmask[from] | piece_setmask[to] | piece_setmask[passant];
			b->all_pieces_rl90 ^= piece_setmask_rl90[from] | piece_setmask_rl90[to] | piece_setmask_rl90[passant];
			b->all_pieces_rl45 ^= piece_setmask_rl45[from] | piece_setmask_rl45[to] | piece_setmask_rl45[passant];
			b->all_pieces_rr45 ^= piece_setmask_rr45[from] | piece_setmask_rr45[to] | piece_setmask_rr45[passant];
		} else {
			*opponent ^= piece_setmask[to];
			b->color[!color].all ^= piece_setmask[to];
			
			b->all_pieces ^= piece_setmask[from];
			b->all_pieces_rl90 ^= piece_setmask_rl90[from];
			b->all_pieces_rl45 ^= piece_setmask_rl45[from];
			b->all_pieces_rr45 ^= piece_setmask_rr45[from];
		}
	}

	// pawn shadow only lasts for one turn
	b->color[color].pawn_shadow = 0x0LLU;
	// leave a pawn shadow if pawn moves two spaces
	if (m.moving_piece == PAWN && ABS(from - to) == 16) {
		if (color == WHITE)
			b->color[WHITE].pawn_shadow = piece_setmask[to-8];
		else
			b->color[BLACK].pawn_shadow = piece_setmask[to+8];
	}

	// move the rook if it's a castle
	if (m.moving_piece == KING) {
		int castle = 0;

		if (from == 4 && to == 6) {
			castle = 1;
			from = 7;
			to = 5;
		} else if (from == 4 && to == 2) {
			castle = 1;
			from = 0;
			to = 3;
		} else if (from == 4+56 && to == 6+56) {
			castle = 1;
			from = 7+56;
			to = 5+56;
		} else if (from == 4+56 && to == 2+56) {
			castle = 1;
			from = 0+56;
			to = 3+56;
		}
		
		if (castle) {
			b->color[color].rooks ^= piece_setmask[from] | piece_setmask[to];
			b->color[color].all ^= piece_setmask[from] | piece_setmask[to];
			b->all_pieces ^= piece_setmask[from] | piece_setmask[to];
			b->all_pieces_rl90 ^= piece_setmask_rl90[from] | piece_setmask_rl90[to];
			b->all_pieces_rl45 ^= piece_setmask_rl45[from] | piece_setmask_rl45[to];
			b->all_pieces_rr45 ^= piece_setmask_rr45[from] | piece_setmask_rr45[to];
		}
	}


	if (!get_attacks_to(b, last_one_64(b->color[color].king), !color)) {
		return 0;
	} else {
		*b = old_b;
		return 1;
	}
}


